package com.example.mediademo.opengl;

import android.graphics.Bitmap;
import android.graphics.SurfaceTexture;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.util.Log;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

public class VideoDrawer implements IDrawder {

    /**
     * 纹理id
     */
    private int mTextureId = -1;
    /**
     * 渲染视频画面
     */
    private SurfaceTexture mSurfaceTexture = null;

    private int glWidth = -1;
    private int glHeight = -1;
    private int videoWidth = -1;
    private int videoHeight = -1;
    /**
     * 坐标变换矩阵
     */
    private float[] mMatrix = null;

    /**
     * 顶点坐标
     */
    private float[] mVertexCoors = new float[]{
            -1f, -1f,
            1f, -1f,
            -1f, 1f,
            1f, 1f};

    /**
     * 顶点坐标
     */
    private float[] mSamllVertexCoors = new float[]{
            0f, -1f,
            1f, -1f,
            0f, 0f,
            1f, 0f};

    /**
     * 纹理坐标
     */
    private float[] mTextureCoors = new float[]{
            0f, 1f,
            1f, 1f,
            0f, 0f,
            1f, 0f};
    /**
     * opelGl程序id
     */
    private int mProgram = -1;
    /**
     * 顶点坐标接收者
     */
    private int mVertexPosHandler = -1;
    /**
     * 纹理坐标接收者
     */
    private int mTexturePosHandler = -1;

    /**
     * 顶点坐标和纹理坐标数据Buffer
     */
    private FloatBuffer mVertexBuffer;
    private FloatBuffer mTextureBuffer;

    private SurfaceTextureCallback surfaceTextureCallback;

    //矩阵变换接收者
    private int mVertexMatrixHandler = -1;
    // 透明值接收者
    private int mAlphaHandler = -1;

    /**
     * 纹理接收者
     */
    private int textureHandler = -1;

    private boolean isSmall;

    private float mAlpha = 1f;
    /**
     * 宽度缩放比例
     */
    private float mWidthRatio = 1f;
    /**
     * 高度缩放比例
     */
    private float mHeightRatio = 1f;

    public VideoDrawer(boolean isSmall) {
        this.isSmall = isSmall;
        initPos();
    }

    /**
     * 初始化顶点坐标
     */
    private void initPos() {

        ByteBuffer bb = ByteBuffer.allocateDirect(mVertexCoors.length * 4);
        bb.order(ByteOrder.nativeOrder());
        // 将坐标数据转换为FloatBuffer,用来传入OpenGL ES程序
        mVertexBuffer = bb.asFloatBuffer();
        mVertexBuffer.put(isSmall ? mSamllVertexCoors : mVertexCoors);
        mVertexBuffer.position(0);

        ByteBuffer bbTexure = ByteBuffer.allocateDirect(mTextureCoors.length * 4);
        bbTexure.order(ByteOrder.nativeOrder());
        mTextureBuffer = bbTexure.asFloatBuffer();
        mTextureBuffer.put(mTextureCoors);
        mTextureBuffer.position(0);
    }

    @Override
    public void setVideoSize(int w, int h) {
        videoWidth = w;
        videoHeight = h;
    }

    @Override
    public void setWindowSize(int w, int h) {
        glWidth = w;
        glHeight = h;
    }

    @Override
    public void draw() {
        if (mTextureId != -1) {
            // 初始化矩阵
            initDefMatrix();
            //【步骤2: 创建、编译并启动OpenGL着色器】
            createGLPrg();
            // 3.激活并绑定纹理单元
            activateTexture();
            // 4.绑定图片到纹理单元
            updateTexture();
            //【5.开始渲染绘制】
            doDraw();
        }
    }

    private void initDefMatrix() {
        if (mMatrix != null) {
            return;
        }
        if (videoWidth != -1 && videoHeight != -1 &&
                glWidth != -1 && glHeight != -1) {
            mMatrix = new float[16];
            float[] resultMatrix = new float[16];
            float videoRatio = (float) videoWidth / (float) videoHeight;
            float glRatio = (float) glWidth / (float) glHeight;
            if (glWidth > glHeight) {
                if (videoRatio > glRatio) {
                    mHeightRatio = videoRatio / glRatio;
                } else {
                    mWidthRatio = glRatio / videoRatio;
                }
            } else {
                if (videoRatio > glRatio) {
                    mHeightRatio = videoRatio / glRatio;
                } else {
                    mWidthRatio = glRatio / videoRatio;
                }
            }
            Matrix.orthoM(
                    resultMatrix, 0,
                    -mWidthRatio, mWidthRatio,
                    -mHeightRatio, mHeightRatio,
                    -1f, 1f);
//            //设置相机位置
            float[] viewMatrix = new float[16];
            Matrix.setLookAtM(
                    viewMatrix, 0,
                    0f, 0f, 2f,
                    0f, 0f, 0f,
                    0f, 1f, 0f
            );
            Matrix.multiplyMM(mMatrix, 0, resultMatrix, 0, viewMatrix, 0);
        }
    }

    /**
     * 平移
     *
     * @param dx x轴移动距离(宽)
     * @param dy y轴移动距离(高)
     */
    public void translate(float dx, float dy) {
        Matrix.translateM(mMatrix, 0, dx*mWidthRatio*2, -dy*mHeightRatio*2, 0f);
    }

    public void transLateM(float dx, float dy) {
        Matrix.translateM(mMatrix, 0, dx, -dy, 0f);
    }

    @Override
    public void setTextureID(int id) {
        this.mTextureId = id;
        mSurfaceTexture = new SurfaceTexture(mTextureId);
        if (surfaceTextureCallback != null) {
            surfaceTextureCallback.surfaceTextureCreate(mSurfaceTexture);
        }
    }

    @Override
    public void release() {
        GLES20.glDisableVertexAttribArray(mVertexPosHandler);
        GLES20.glDisableVertexAttribArray(mTexturePosHandler);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
        GLES20.glDeleteTextures(1, new int[]{mTextureId}, 0);
        GLES20.glDeleteProgram(mProgram);
    }

    @Override
    public void getSurfaceTexture(SurfaceTextureCallback surfaceTextureCallback) {
        this.surfaceTextureCallback = surfaceTextureCallback;
    }

    @Override
    public void setAlpha(float alpha) {
        mAlpha = alpha;
    }

    /**
     * 创建opengl程序
     */
    private void createGLPrg() {
        if (mProgram == -1) {
            int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, getVertexShaderCode());
            int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, getFragmentShaderCode());

            //创建OpenGL ES程序，注意：需要在OpenGL渲染线程中创建，否则无法渲染
            mProgram = GLES20.glCreateProgram();
            //将顶点着色器加入到程序
            GLES20.glAttachShader(mProgram, vertexShader);
            //将片元着色器加入到程序中
            GLES20.glAttachShader(mProgram, fragmentShader);
            //连接到着色器程序
            GLES20.glLinkProgram(mProgram);
            //【新增2: 获取顶点着色器中的矩阵变量】
            mVertexMatrixHandler = GLES20.glGetUniformLocation(mProgram, "uMatrix");
            mVertexPosHandler = GLES20.glGetAttribLocation(mProgram, "aPosition");
            mTexturePosHandler = GLES20.glGetAttribLocation(mProgram, "aCoordinate");
            //【注3：新增获取纹理接收者】
            textureHandler = GLES20.glGetUniformLocation(mProgram, "uTexture");
            // 新增透明接收者
            mAlphaHandler = GLES20.glGetAttribLocation(mProgram, "alpha");
        }
        //使用OpenGL程序
        GLES20.glUseProgram(mProgram);
    }

    /**
     * 激活纹理单元 如果是渲染普通的Bitmap,是GLES20.GL_TEXTURE_2D
     * 扩展的纹理单元：GLES11Ext.GL_TEXTURE_EXTERNAL_OES
     */
    private void activateTexture() {
        //激活指定纹理单元
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        //绑定纹理ID到纹理单元
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureId);
        //将激活的纹理单元传递到着色器里面
        GLES20.glUniform1i(textureHandler, 0);
        //配置纹理过滤模式
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, (float) GLES20.GL_LINEAR);
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, (float) GLES20.GL_LINEAR);
        // 配置纹理环绕方式
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
    }

//    private void bindBitmapToTexture() {
//        if (!mBitmap.isRecycled()) {
//            // 绑定图片到被激活的纹理单元
//            GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmap, 0);
//        }
//    }

    private void updateTexture() {
        if (mSurfaceTexture != null) {
            mSurfaceTexture.updateTexImage();
        }
    }


    private void doDraw() {
        //启用顶点的句柄
        GLES20.glEnableVertexAttribArray(mVertexPosHandler);
        GLES20.glEnableVertexAttribArray(mTexturePosHandler);
        GLES20.glUniformMatrix4fv(mVertexMatrixHandler, 1, false, mMatrix, 0);
        //设置着色器参数 第二个参数表示一个顶点包含的数据数量，这里为xy，所以为2
        GLES20.glVertexAttribPointer(mVertexPosHandler, 2, GLES20.GL_FLOAT, false, 0, mVertexBuffer);
        GLES20.glVertexAttribPointer(mTexturePosHandler, 2, GLES20.GL_FLOAT, false, 0, mTextureBuffer);
        // 设置透明度参数
        GLES20.glVertexAttrib1f(mAlphaHandler, mAlpha);
        //开始绘制
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
    }


    /**
     * 加载着色器
     *
     * @param type       着色器类别
     * @param shaderCode 着色器代码
     * @return
     */
    private int loadShader(int type, String shaderCode) {
        //根据type创建顶点着色器或者片元着色器
        int shader = GLES20.glCreateShader(type);
        //将资源加入到着色器中，并编译
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);
        return shader;
    }

    /**
     * 获取顶点着色器代码字符串
     *
     * @return
     */
    private String getVertexShaderCode() {
        return "attribute vec4 aPosition;" +
                "precision mediump float;" +
                "uniform mat4 uMatrix;" +
                "attribute vec2 aCoordinate;" +
                "varying vec2 vCoordinate;" +
                "attribute float alpha;" +
                "varying float inAlpha;" +
                "void main() {" +
                "    gl_Position = aPosition*uMatrix;" +
                "    vCoordinate = aCoordinate;" +
                "    inAlpha = alpha;" +
                "}";
    }

    /**
     * 获取片元着色器代码字符串
     * sampleExternalOES 扩展纹理单元 YUV->RGB用的 sample2D RGB纹理单元
     *
     * @return
     */
    private String getFragmentShaderCode() {
        // RGBA
        // 一定要加换行"\n"，否则会和下一行的precision混在一起，导致编译出错
        return "#extension GL_OES_EGL_image_external : require\n" +
                "precision mediump float;" +
                "varying float inAlpha;" +
                "uniform samplerExternalOES uTexture;" +
                "varying vec2 vCoordinate;" +
                "void main() {" +
                "  vec4 color = texture2D(uTexture, vCoordinate);" +
                "  float gray = (color.r + color.g + color.b)/3.0;" +
                "  gl_FragColor = vec4(color.r, color.g, color.b, inAlpha);" +
                "}";
    }


}
